#include "execute.h"
#include "helper.h"

#include <map>
#include <string>

void (*ori_execute_ex)(zend_execute_data *execute_data) = nullptr;
void (*ori_execute_internal)(zend_execute_data *execute_data, zval *return_value) = nullptr;

// curl
void monitorpro_curl_setopt_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
void monitorpro_curl_setopt_array_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
void monitorpro_pre_curl_exec_handler(zend_execute_data *execute_data, zval *return_value, std::string class_name, std::string function_name);
void monitorpro_post_curl_exec_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
void monitorpro_curl_close_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
// predis
extern void monitorpro_predis_handler(zend_execute_data *execute_data, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
// phpredis
extern void monitorpro_phpredis_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
// pdo
extern void monitorpro_pdo_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
extern void monitorpro_pdo_stmt_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
// mysqli
extern void monitorpro_mysqli_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
extern void monitorpro_mysqli_obj_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
extern void monitorpro_mysqli_stmt_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);
extern void monitorpro_mysqli_stmt_obj_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name);

std::map<std::string, pre_internal_func> pre_exec_internal_func = {
    // {"null::curl_exec", monitorpro_pre_curl_exec_handler},
};
std::map<std::string, post_internal_func> post_exec_internal_func = {
    {"null::curl_setopt", monitorpro_curl_setopt_handler},
    {"null::curl_setopt_array", monitorpro_curl_setopt_array_handler},
    {"null::curl_exec", monitorpro_post_curl_exec_handler},
    // {"null::curl_close", monitorpro_curl_close_handler},
    {"Redis::*", monitorpro_phpredis_handler},
    {"PDO::query", monitorpro_pdo_handler},
    {"PDO::exec", monitorpro_pdo_handler},
    {"PDO::prepare", monitorpro_pdo_handler},
    {"PDOStatement::execute", monitorpro_pdo_stmt_handler},
    {"null::mysqli_query", monitorpro_mysqli_handler},
    {"null::mysqli_prepare", monitorpro_mysqli_handler},
    {"mysqli::prepare", monitorpro_mysqli_obj_handler},
    {"null::mysqli_stmt_execute", monitorpro_mysqli_stmt_handler},
    {"mysqli_stmt::execute", monitorpro_mysqli_stmt_obj_handler},
};
std::map<std::string, pre_ex_func> pre_exec_ex_func = {};
std::map<std::string, post_ex_func> post_exec_ex_func = {
    {"Predis\\Client::__call", monitorpro_predis_handler},
};

void monitorpro_execute_internal(zend_execute_data *execute_data, zval *return_value)
{
    // 获取开始执行时间
    struct timeval tv;
    long start_micro_time, end_micro_time;
    gettimeofday(&tv, NULL);
    start_micro_time = tv.tv_sec * 1000000 + tv.tv_usec;

    // 获取类名和方法名
    std::string cn, fn, cf, cf_all;
    zend_function *func = execute_data->func;
    if (func != nullptr)
    {
        int is_class = func->common.scope != nullptr && func->common.scope->name != nullptr;
        char *class_name = is_class ? ZSTR_VAL(func->common.scope->name) : nullptr;
        char *function_name = func->common.function_name != nullptr ? ZSTR_VAL(func->common.function_name) : nullptr;

        cn = class_name ? class_name : "null";
        fn = function_name ? function_name : "null";
        cf = cn + "::" + fn;
        cf_all = cn + "::*";
    }

    // 前置操作
    if (pre_exec_internal_func.count(cf) > 0)
    {
        logger("[pre exec internal] " + cf);
        pre_exec_internal_func[cf](execute_data, return_value, cn, fn);
    }
    else if (pre_exec_internal_func.count(cf_all) > 0)
    {
        logger("[pre exec internal] " + cf_all);
        pre_exec_internal_func[cf_all](execute_data, return_value, cn, fn);
    }

    if (ori_execute_internal)
    {
        ori_execute_internal(execute_data, return_value);
    }
    else
    {
        execute_internal(execute_data, return_value);
    }

    // 获取执行结束时间
    gettimeofday(&tv, NULL);
    end_micro_time = tv.tv_sec * 1000000 + tv.tv_usec;

    // 后置操作
    if (post_exec_internal_func.count(cf) > 0)
    {
        logger("[post exec internal] " + cf);
        post_exec_internal_func[cf](execute_data, return_value, start_micro_time, end_micro_time, cn, fn);
    }
    else if (post_exec_internal_func.count(cf_all) > 0)
    {
        logger("[post exec internal] " + cf_all);
        post_exec_internal_func[cf_all](execute_data, return_value, start_micro_time, end_micro_time, cn, fn);
    }
}

void monitorpro_execute_ex(zend_execute_data *execute_data)
{
    // 获取开始执行时间
    struct timeval tv;
    long start_micro_time, end_micro_time;
    gettimeofday(&tv, NULL);
    start_micro_time = tv.tv_sec * 1000000 + tv.tv_usec;

    // 获取类名和方法名
    std::string cn, fn, cf, cf_all;
    zend_function *func = execute_data->func;
    if (func != nullptr)
    {
        int is_class = func->common.scope != nullptr && func->common.scope->name != nullptr;
        char *class_name = is_class ? ZSTR_VAL(func->common.scope->name) : nullptr;
        char *function_name = func->common.function_name != nullptr ? ZSTR_VAL(func->common.function_name) : nullptr;

        cn = class_name ? class_name : "null";
        fn = function_name ? function_name : "null";
        cf = cn + "::" + fn;
        cf_all = cn + "::*";
    }

    // 前置操作
    if (pre_exec_ex_func.count(cf) > 0)
    {
        logger("[pre exec ex] " + cf);
        pre_exec_ex_func[cf](execute_data, cn, fn);
    }
    else if (pre_exec_ex_func.count(cf_all) > 0)
    {
        logger("[pre exec ex] " + cf_all);
        pre_exec_ex_func[cf_all](execute_data, cn, fn);
    }

    // 执行原方法
    ori_execute_ex(execute_data);

    // 获取执行结束时间
    gettimeofday(&tv, NULL);
    end_micro_time = tv.tv_sec * 1000000 + tv.tv_usec;

    // 后置操作
    if (post_exec_ex_func.count(cf) > 0)
    {
        logger("[post exec ex] " + cf);
        post_exec_ex_func[cf](execute_data, start_micro_time, end_micro_time, cn, fn);
    }
    else if (post_exec_ex_func.count(cf_all) > 0)
    {
        logger("[post exec ex] " + cf_all);
        post_exec_ex_func[cf_all](execute_data, start_micro_time, end_micro_time, cn, fn);
    }
}